<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"><title>集合 | 江一行</title><meta name="author" content="江一行"><meta name="copyright" content="江一行"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="referrer" content="no-referrer"><meta name="description" content="概述Java 中的集合类主要由 Collection 和 Map 这两个接口派生而出，其中 Collection 接口又派生出三个子接口，分别是 Set、List、Queue。所有的 Java 集合类，都是 Set、List、Queue、Map 这四个接口的实现类，这四个接口将集合分成了四大类，其中  Set 代表无序的，元素不可重复的集合； List 代表有序的，元素可以重复的集合； Queue">
<meta property="og:type" content="article">
<meta property="og:title" content="集合">
<meta property="og:url" content="https://j_yh.gitee.io/Java/%E9%9B%86%E5%90%88/index.html">
<meta property="og:site_name" content="江一行">
<meta property="og:description" content="概述Java 中的集合类主要由 Collection 和 Map 这两个接口派生而出，其中 Collection 接口又派生出三个子接口，分别是 Set、List、Queue。所有的 Java 集合类，都是 Set、List、Queue、Map 这四个接口的实现类，这四个接口将集合分成了四大类，其中  Set 代表无序的，元素不可重复的集合； List 代表有序的，元素可以重复的集合； Queue">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://w.wallhaven.cc/full/lm/wallhaven-lmwg12.jpg">
<meta property="article:published_time" content="2023-02-27T16:22:33.000Z">
<meta property="article:modified_time" content="2023-02-28T11:48:57.600Z">
<meta property="article:author" content="江一行">
<meta property="article:tag" content="面试">
<meta property="article:tag" content="集合">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://w.wallhaven.cc/full/lm/wallhaven-lmwg12.jpg"><link rel="shortcut icon" href="/img/favicon.ico"><link rel="canonical" href="https://j_yh.gitee.io/Java/%E9%9B%86%E5%90%88/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":200},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"bottom-left"},
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: false,
  }
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '集合',
  isPost: true,
  isHome: false,
  isHighlightShrink: true,
  isToc: true,
  postUpdate: '2023-02-28 19:48:57'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.3.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/img/avatar.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">7</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">5</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">2</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://w.wallhaven.cc/full/lm/wallhaven-lmwg12.jpg')"><nav id="nav"><span id="blog-info"><a href="/" title="江一行"><span class="site-name">江一行</span></a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">集合</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2023-02-27T16:22:33.000Z" title="发表于 2023-02-28 00:22:33">2023-02-28</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-02-28T11:48:57.600Z" title="更新于 2023-02-28 19:48:57">2023-02-28</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Java/">Java</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">5k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>15分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="集合"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span><span class="post-meta-separator">|</span><span class="post-meta-commentcount"><i class="far fa-comments fa-fw post-meta-icon"></i><span class="post-meta-label">评论数:</span><a href="/Java/%E9%9B%86%E5%90%88/#post-comment" itemprop="discussionUrl"><span class="valine-comment-count" data-xid="/Java/%E9%9B%86%E5%90%88/" itemprop="commentCount"><i class="fa-solid fa-spinner fa-spin"></i></span></a></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>Java 中的集合类主要由 Collection 和 Map 这两个接口派生而出，其中 Collection 接口又派生出三个子接口，分别是 Set、List、Queue。所有的 Java 集合类，都是 Set、List、Queue、Map 这四个接口的实现类，这四个接口将集合分成了四大类，其中</p>
<ul>
<li>Set 代表无序的，元素不可重复的集合；</li>
<li>List 代表有序的，元素可以重复的集合；</li>
<li>Queue 代表先进先出（FIFO）的队列；</li>
<li>Map 代表具有映射关系（key-value）的集合。其所有的 key 是一个 Set 集合，即 key 无序且不能重复。</li>
</ul>
<p>这些接口拥有众多的实现类，其中最常用的实现类有 HashSet、TreeSet、ArrayList、LinkedList、ArrayDeque、HashMap、TreeMap 等。</p>
<p>Collection体系：</p>
<p><img src="https://gitee.com/j_yh/typroa/raw/master/images/2023/02/20230228193724-.jpg" alt="collection"></p>
<p>Map体系：</p>
<p><img src="https://gitee.com/j_yh/typroa/raw/master/images/2023/02/20230228193804.jpg" alt="map"></p>
<p><em>紫色框体代表接口，其中加粗的是代表四类集合的接口。蓝色框体代表实现类，其中有阴影的是常用实现类。</em></p>
<h2 id="Java-容器线程安全问题"><a href="#Java-容器线程安全问题" class="headerlink" title="Java 容器线程安全问题"></a>Java 容器线程安全问题</h2><p>java.util 包下的集合类大部分都是线程不安全的，例如常用的 HashSet、TreeSet、ArrayList、LinkedList、ArrayDeque、HashMap、TreeMap，这些都是线程不安全的集合类，但是它们的优点是性能好。如果需要使用线程安全的集合类，则可以使用 Collections 工具类提供的synchronizedXxx()方法，将这些集合类包装成线程安全的集合类。</p>
<p>java.util 包下也有线程安全的集合类，例如 Vector、Hashtable。这些集合类都是比较古老的 API，虽然实现了线程安全，但是性能很差。不建议直接使用这些古老的 API。</p>
<p>从 Java5 开始，Java 在 java.util.concurrent 包下提供了大量支持高效并发访问的集合类，它们既能包装良好的访问性能，有能包装线程安全。这些集合类可以分为两部分，它们的特征如下：</p>
<ul>
<li><p>以 Concurrent 开头的集合类：</p>
<p>以 Concurrent 开头的集合类代表了支持并发访问的集合，它们可以支持多个线程并发写入访问，这些写入线程的所有操作都是线程安全的，但读取操作不必锁定。以 Concurrent 开头的集合类采用了更复杂的算法来保证永远不会锁住整个集合，因此在并发写入时有较好的性能。</p>
</li>
<li><p>以 CopyOnWrite 开头的集合类：</p>
<p>以 CopyOnWrite 开头的集合类采用复制底层数组的方式来实现写操作。当线程对此类集合执行读取操作时，线程将会直接读取集合本身，无须加锁与阻塞。当线程对此类集合执行写入操作时，集合会在底层复制一份新的数组，接下来对新的数组执行写入操作。由于对集合的写入操作都是对数组的副本执行操作，因此它是线程安全的。</p>
</li>
</ul>
<h2 id="HashMap"><a href="#HashMap" class="headerlink" title="HashMap"></a>HashMap</h2><blockquote>
<ol>
<li>HashMap 是线程不安全的实现；</li>
<li>HashMap 可以使用 null 作为 key 或 value。</li>
</ol>
</blockquote>
<h3 id="JDK7-与-JDK8-之间的区别"><a href="#JDK7-与-JDK8-之间的区别" class="headerlink" title="JDK7 与 JDK8 之间的区别"></a>JDK7 与 JDK8 之间的区别</h3><p>JDK7 中的 HashMap，是基于数组+链表来实现的，它的底层维护一个 Entry 数组。它会根据计算的 hashCode 将对应的 KV 键值对存储到该数组中，一旦发生hashCode 冲突，那么就会将该 KV 键值对放到对应的已有元素的后面， 此时便形成了一个链表式的存储结构。</p>
<p>JDK7 中 HashMap 的实现方案有一个明显的缺点，即当 Hash 冲突严重时，在桶上形成的链表会变得越来越长，这样在查询时的效率就会越来越低，其时间复杂度为O(N)。</p>
<p>JDK8中的 HashMap，是基于数组+链表+红黑树来实现的，它的底层维护一个 Node 数组。当链表的存储的数据个数大于等于 8 的时候，不再采用链表存储（还需要 table 表容量 &gt;&#x3D; 64），而采用了红黑树存储结构。这么做主要是在查询的时间复杂度上进行优化，链表为O(N)，而红黑树一直是 O(logN)，可以大大的提高查找性能。</p>
<h3 id="实现原理"><a href="#实现原理" class="headerlink" title="实现原理"></a>实现原理</h3><p>基于 hash 算法，通过 put() 和 get() 方法存储和获取对象。</p>
<p>存储对象时，我们将 K&#x2F;V 传给 put 方法时，它调用 K 的 hashCode 计算 hash 从而得到 bucket 位置，进一步存储，HashMap 会根据当前 bucket 的占用情况自动调整容量(超过 Load Facotr 则 resize 为原来的2倍)。获取对象时，将 K 传给 get，它调用 hashCode 计算 hash 从而得到 bucket 位置，并进一步调用 equals() 方法确定键值对。</p>
<p>如果发生碰撞的时候，HashMap 通过链表将产生碰撞冲突的元素组织起来。在 Java 8 中，如果一个 bucket 中碰撞冲突的元素超过某个限制(默认是8，需要考虑容量是否大于阈值 64 )，则使用红黑树来替换链表，从而提高速度。</p>
<h3 id="HashMap的扩容机制"><a href="#HashMap的扩容机制" class="headerlink" title="HashMap的扩容机制"></a>HashMap的扩容机制</h3><ol>
<li>数组的初始容量为 16，而容量是以 2 的次方扩充的，一是为了提高性能使用足够大的数组，二是为了能使用位运算代替取模预算(据说提升了 5~8 倍)。</li>
<li>数组是否需要扩充是通过负载因子判断的，如果当前元素个数为数组容量的 0.75 时，就会扩充数组。这个 0.75 就是默认的负载因子，可由构造器传入。我们也可以设置大于 1 的负载因子，这样数组就不会扩充，牺牲性能，节省内存。</li>
<li>为了解决碰撞，数组中的元素是单向链表类型。当链表长度到达一个阈值时（7或8），会将链表转换成红黑树提高性能。而当链表长度缩小到另一个阈值时（6），又会将红黑树转换回单向链表提高性能。</li>
<li>对于第三点补充说明，检查链表长度转换成红黑树之前，还会先检测当前数组数组是否到达一个阈值（64），如果没有到达这个容量，会放弃转换，先去扩充数组。所以上面也说了链表长度的阈值是 7 或 8，因为会有一次放弃转换的操作。</li>
</ol>
<p>收下我的膝盖：</p>
<p>例如从16 扩展为 32 时，具体的变化如下所示：</p>
<p><img src="https://gitee.com/j_yh/typroa/raw/master/images/2023/02/20230228193830.png" alt="hashmap-1"></p>
<p>因此元素在重新计算 has h之后，因为 n 变为 2 倍，那么 n-1 的 mask 范围在高位多 1 bit(红色)，因此新的 index 就会发生这样的变化：</p>
<p><img src="https://gitee.com/j_yh/typroa/raw/master/images/2023/02/20230228193837.png" alt="hashmap-2"></p>
<p>因此，我们在扩充 HashMap 的时候，不需要重新计算 hash，只需要看看原来的 hash 值新增的那个 bit 是 1 还是 0，是 0 的话索引没变，是 1 的话索引变成“原索引+oldCap”。这个设计确实非常的巧妙，既省去了重新计算 hash 值的时间，而且同时，由于新增的 1bit 是 0 还是 1 可以认为是随机的，因此 resize 的过程，均匀的把之前的冲突的节点分散到新的 bucket。</p>
<h3 id="HashMap-和-HashTable-的区别"><a href="#HashMap-和-HashTable-的区别" class="headerlink" title="HashMap 和 HashTable 的区别"></a>HashMap 和 HashTable 的区别</h3><ol>
<li>Hashtable 是一个线程安全的 Map 实现，但 HashMap 是线程不安全的实现，所以 HashMap 比 Hashtable 的性能高一点。</li>
<li>Hashtable 不允许使用 null 作为 key 和 value，如果试图把 null 值放进 Hashtable 中，将会引发空指针异常，但 HashMap 可以使用 null 作为 key 或 value。</li>
</ol>
<p>从 Hashtable 的类名上就可以看出它是一个古老的类，它的命名甚至没有遵守 Java 的命名规范：每个单词的首字母都应该大写。也许当初开发 Hashtable 的工程师也没有注意到这一点，后来大量 Java 程序中使用了 Hashtable 类，所以这个类名也就不能改为 HashTable 了，否则将导致大量程序需要改写。</p>
<p>与 Vector 类似的是，尽量少用 Hashtable 实现类，即使需要创建线程安全的 Map 实现类，也无须使用 Hashtable 实现类，可以通过 Collections 工具类把 HashMap 变成线程安全的 Map。</p>
<h3 id="HashMap-与-ConcurrentHashMap-的区别"><a href="#HashMap-与-ConcurrentHashMap-的区别" class="headerlink" title="HashMap  与 ConcurrentHashMap 的区别"></a>HashMap  与 ConcurrentHashMap 的区别</h3><p>HashMap 是非线程安全的，这意味着不应该在多线程中对这些 Map 进行修改操作，否则会产生数据不一致的问题，甚至还会因为并发插入元素而导致链表成环，这样在查找时就会发生死循环，影响到整个应用程序。</p>
<p>Collections 工具类可以将一个 Map 转换成线程安全的实现，其实也就是通过一个包装类，然后把所有功能都委托给传入的Map，而包装类是基于 synchronized 关键字来保证线程安全的（Hashtable 也是基于 synchronized 关键字），底层使用的是互斥锁，性能与吞吐量比较低。</p>
<p>ConcurrentHashMap 的实现细节远没有这么简单，因此性能也要高上许多。它没有使用一个全局锁来锁住自己，而是采用了减少锁粒度的方法，尽量减少因为竞争锁而导致的阻塞与冲突，而且 ConcurrentHashMap 的检索操作是不需要锁的。</p>
<h2 id="ConcurrentHashMap"><a href="#ConcurrentHashMap" class="headerlink" title="ConcurrentHashMap"></a>ConcurrentHashMap</h2><p>ConcurrentHashMap 的具体原理，<a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV15b4y117RJ?p=86&vd_source=4e4bd752f93656ba2224ee39b464d8a6">黑马程序员的视频</a> 中对具体实现、扩容机制等情况进行了说明。</p>
<h3 id="JDK-1-7-中的实现："><a href="#JDK-1-7-中的实现：" class="headerlink" title="JDK 1.7 中的实现："></a>JDK 1.7 中的实现：</h3><p>在 jdk 1.7 中，ConcurrentHashMap 是由 Segment 数据结构和 HashEntry 数组结构构成，采取分段锁来保证安全性。Segment 是 ReentrantLock 重入锁，在 ConcurrentHashMap 中扮演锁的角色，HashEntry 则用于存储键值对数据。一个 ConcurrentHashMap 里包含一个 Segment 数组，一个 Segment 里包含一个 HashEntry 数组，Segment 的结构和 HashMap 类似，是一个数组和链表结构。Segment 数组大小对应支持线程并发访问个数。</p>
<p><img src="https://gitee.com/j_yh/typroa/raw/master/images/2023/02/20230228194803.png" alt="20230228194803"></p>
<h3 id="JDK-1-8中的实现："><a href="#JDK-1-8中的实现：" class="headerlink" title="JDK 1.8中的实现："></a>JDK 1.8中的实现：</h3><p>JDK1.8 的实现已经摒弃了 Segment 的概念，用 Node 数组+链表+红黑树的数据结构来实现，并发控制使用 Synchronized 和 CAS 来操作，看起来就像是优化过且线程安全的 HashMap，虽然在 JDK1.8 中还能看到 Segment 的数据结构，但是已经简化了属性，只是为了兼容旧版本。</p>
<h3 id="分段分组"><a href="#分段分组" class="headerlink" title="分段分组"></a>分段分组</h3><p>get操作：</p>
<p>Segment的get操作实现非常简单和高效，先经过一次再散列，然后使用这个散列值通过散列运算定位到 Segment，再通过散列算法定位到元素。get 操作的高效之处在于整个 get 过程都不需要加锁，除非读到空的值才会加锁重读。原因就是将使用的共享变量定义成 <code>volatile</code> 类型。</p>
<p>put操作：</p>
<p>当执行put操作时，会经历两个步骤：</p>
<ol>
<li>判断是否需要扩容；</li>
<li>定位到添加元素的位置，将其放入 HashEntry 数组中。</li>
</ol>
<p>插入过程会进行第一次 key 的 hash 来定位 Segment 的位置，如果该 Segment 还没有初始化，即通过 CAS 操作进行赋值，然后进行第二次 hash 操作，找到相应的 HashEntry 的位置，这里会利用继承过来的锁的特性，在将数据插入指定的 HashEntry 位置时（尾插法），会通过继承 ReentrantLock 的 <code>tryLock()</code> 方法尝试去获取锁，如果获取成功就直接插入相应的位置，如果已经有线程获取该Segment的锁，那当前线程会以自旋的方式去继续的调用 <code>tryLock()</code> 方法去获取锁，超过指定次数就挂起，等待唤醒。</p>
<h2 id="LinkedHashMap"><a href="#LinkedHashMap" class="headerlink" title="LinkedHashMap"></a>LinkedHashMap</h2><p>LinkedHashMap 使用双向链表来维护 key-value 对的顺序（只需要考虑key的顺序），该链表负责维护 Map 的迭代顺序，迭代顺序与 key-value 对的插入顺序保持一致。</p>
<p>LinkedHashMap 可以避免对 HashMap、Hashtable 里的 key-value 对进行排序（只要插入 key-value 对时保持顺序即可），同时又可避免使用 TreeMap 所增加的成本。</p>
<p>LinkedHashMap 需要维护元素的插入顺序，因此性能略低于 HashMap。但因为它以链表来维护内部顺序，所以在迭代访问 Map 里的全部元素时将有较好的性能。</p>
<p>底层原理：</p>
<p>LinkedHashMap 继承于 HashMap，它在 HashMap 的基础上，通过<strong>维护一条双向链表</strong>，解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题。在实现上，LinkedHashMap 很多方法直接继承自HashMap，仅为维护双向链表重写了部分方法。</p>
<h2 id="TeeMap"><a href="#TeeMap" class="headerlink" title="TeeMap"></a>TeeMap</h2><p>TreeMap 基于红黑树（Red-Black tree）实现。映射根据其键的自然顺序进行排序，或者根据创建映射时提供的 Comparator 进行排序，具体取决于使用的构造方法。TreeMap 的基本方法：containsKey、get、put、remove，它的时间复杂度是log(N)。</p>
<p>TreeMap 包含几个重要的成员变量：root、size、comparator。其中root是红黑树的根节点。它是 Entry 类型，Entry 是红黑树的节点，它包含了红黑树的 6 个基本组成：key、value、left、right、parent和color。Entry 节点根据根据 Key 排序，包含的内容是 value。Entry 中 key 比较大小是根据比较器comparator来进行判断的。size 是红黑树的节点个数。</p>
<h2 id="ArrayList"><a href="#ArrayList" class="headerlink" title="ArrayList"></a>ArrayList</h2><p>ArrayList 的底层是用数组来实现的，默认第一次<strong>插入元素时创建</strong>大小为10的数组，超出限制时会增加 50% 的容量，并且数据以 <code>System.arraycopy()</code> 复制到新的数组，因此最好能给出数组大小的预估值。</p>
<p>按数组下标访问元素的性能很高，这是数组的基本优势。直接在数组末尾加入元素的性能也高，但如果按下标插入、删除元素，则要用 <code>System.arraycopy()</code> 来移动部分受影响的元素，性能就变差了，这是基本劣势。</p>
<h3 id="与-LinkedList-的区别"><a href="#与-LinkedList-的区别" class="headerlink" title="与 LinkedList 的区别"></a>与 LinkedList 的区别</h3><ol>
<li>ArrayList 的实现是基于数组，LinkedList 的实现是基于双向链表；</li>
<li>对于随机访问 ArrayList 要优于 LinkedList，ArrayList 可以根据下标以 O(1) 时间复杂度对元素进行随机访问，而LinkedList 的每一个元素都依靠地址指针和它后一个元素连接在一起，查找某个元素的时间复杂度是 O(N)；</li>
<li>对于插入和删除操作，LinkedList 要优于 ArrayList，因为当元素被添加到 LinkedList 任意位置的时候，不需要像ArrayList 那样重新计算大小或者是更新索引；</li>
<li>LinkedList 比 ArrayList 更占内存，因为 LinkedList 的节点除了存储数据，还存储了两个引用，一个指向前一个元素，一个指向后一个元素。</li>
</ol>
<h3 id="CopyOnWriteArrayList"><a href="#CopyOnWriteArrayList" class="headerlink" title="CopyOnWriteArrayList"></a>CopyOnWriteArrayList</h3><p>CopyOnWriteArrayList 是 Java 并发包里提供的，简单来说它就是一个<strong>线程安全且读操作无锁</strong>的 ArrayList。正如其名字一样，在写操作时会复制一份新的 List，在新的 List上 完成写操作，然后再将原引用指向新的List。这样就保证了写操作的线程安全。</p>
<p>CopyOnWriteArrayList 允许线程并发访问读操作，这个时候是没有加锁限制的，性能较高。而写操作的时候，则首先将容器复制一份，然后在新的副本上执行写操作，这个时候写操作是上锁的。结束之后再将原容器的引用指向新容器。注意，在上锁执行写操作的过程中，如果有需要读操作，会作用在原容器上。因此上锁的写操作不会影响到并发访问的读操作。</p>
<ul>
<li>优点：读操作性能很高，因为无需任何同步措施，比较适用于<strong>读多写少</strong>的并发场景。在遍历传统的 List 时，若中途有别的线程对其进行修改，则会抛出 ConcurrentModificationException 异常。而 CopyOnWriteArrayList 由于其”读写分离”的思想，遍历和修改操作分别作用在不同的List容器，所以在使用迭代器进行遍历时候，也就不会抛出 ConcurrentModificationException 异常了。</li>
<li>缺点：一是内存占用问题，每次执行写操作都要将原容器拷贝一份，数据量大时，对内存压力较大，可能会引起频繁GC。二是无法保证实时性，Vector 对于读写操作均加锁同步，可以保证读和写的强一致性。而 CopyOnWriteArrayList 由于其实现策略的原因，写和读分别作用在新老不同容器上，在写操作执行过程中，读不会阻塞但读取到的却是老容器的数据。</li>
</ul>
<h2 id="HashSet"><a href="#HashSet" class="headerlink" title="HashSet"></a>HashSet</h2><p>HashSet 是基于 HashMap 实现的，默认构造函数是构建一个初始容量为 16，负载因子为 0.75 的HashMap。它封装了一个 HashMap 对象来存储所有的集合元素，所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存，而 HashMap 的 value 则存储了一个 PRESENT，它是一个静态的 Object 对象。</p>
<h3 id="与-TreeSet-的区别"><a href="#与-TreeSet-的区别" class="headerlink" title="与 TreeSet 的区别"></a>与 TreeSet 的区别</h3><p>HashSet、TreeSet 中的元素都是不能重复的，并且它们都是线程不安全的，二者的区别是：</p>
<ol>
<li>HashSet 中的元素可以是 null，但 TreeSet中 的元素不能是 null；</li>
<li>HashSet 不能保证元素的排列顺序，而 TreeSet 支持自然排序、定制排序两种排序的方式；</li>
<li>HashSet 底层是采用哈希表实现的，而 TreeSet 底层是采用红黑树实现的。</li>
</ol>
<h2 id="Stream-流"><a href="#Stream-流" class="headerlink" title="Stream 流"></a>Stream 流</h2><p>Stream提供了大量的方法进行聚集操作，这些方法既可以是“中间的”，也可以是“末端的”。</p>
<ul>
<li>中间方法：中间操作允许流保持打开状态，并允许直接调用后续方法。上面程序中的 map() 方法就是中间方法。中间方法的返回值是另外一个流。</li>
<li>末端方法：末端方法是对流的最终操作。当对某个 Stream 执行末端方法后，该流将会被“消耗”且不再可用。上面程序中的 sum()、count()、average()等方法都是末端方法。</li>
</ul>
<p>Stream 常用的中间方法：</p>
<ul>
<li>filter(Predicate predicate)：过滤 Stream 中所有不符合 predicate 的元素。</li>
<li>mapToXxx(ToXxxFunction mapper)：使用 ToXxxFunction 对流中的元素执行一对一的转换，该方法返回的新流中包含了 ToXxxFunction 转换生成的所有元素。</li>
<li>peek(Consumer action)：依次对每个元素执行一些操作，该方法返回的流与原有流包含相同的元素。该方法主要用于调试。</li>
<li>distinct()：该方法用于排序流中所有重复的元素（判断元素重复的标准是使用equals()比较返回 true）。这是一个有状态的方法。</li>
<li>sorted()：该方法用于保证流中的元素在后续的访问中处于有序状态。这是一个有状态的方法。</li>
<li>limit(long maxSize)：该方法用于保证对该流的后续访问中最大允许访问的元素个数。这是一个有状态的、短路方法。</li>
</ul>
<p>Stream 常用的末端方法：</p>
<ul>
<li>forEach(Consumer action)：遍历流中所有元素，对每个元素执行 action。</li>
<li>toArray()：将流中所有元素转换为一个数组。</li>
<li>reduce()：该方法有三个重载的版本，都用于通过某种操作来合并流中的元素。</li>
<li>min()：返回流中所有元素的最小值。</li>
<li>max()：返回流中所有元素的最大值。</li>
<li>count()：返回流中所有元素的数量。</li>
<li>anyMatch(Predicate predicate)：判断流中是否至少包含一个元素符合 Predicate条件。</li>
<li>noneMatch(Predicate predicate)：判断流中是否所有元素都不符合 Predicate条件。</li>
<li>findFirst()：返回流中的第一个元素。</li>
<li>findAny()：返回流中的任意一个元素。</li>
</ul>
<p>除此之外，Java 8 允许使用流式 API 来操作集合，Collection接口提供了一个 stream() 默认方法，该方法可返回该集合对应的流，接下来即可通过流式 API 来操作集合元素。由于 Stream 可以对集合元素进行整体的聚集操作，因此 Stream 极大地丰富了集合的功能。</p>
<p><em>具体使用可参考别人的 <a target="_blank" rel="noopener" href="https://blog.csdn.net/m0_60489526/article/details/119984236">博客</a></em></p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://j_yh.gitee.io/">江一行</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://j_yh.gitee.io/Java/%E9%9B%86%E5%90%88/">https://j_yh.gitee.io/Java/%E9%9B%86%E5%90%88/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://j_yh.gitee.io" target="_blank">江一行</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/%E9%9D%A2%E8%AF%95/">面试</a><a class="post-meta__tags" href="/tags/%E9%9B%86%E5%90%88/">集合</a></div><div class="post_share"><div class="social-share" data-image="https://w.wallhaven.cc/full/lm/wallhaven-lmwg12.jpg" data-sites="wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/Java/Java%E5%9F%BA%E7%A1%80/" title="Java 基础"><img class="cover" src="https://w.wallhaven.cc/full/4o/wallhaven-4oovr5.png" onerror="onerror=null;src='/img/404.png'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Java 基础</div></div></a></div><div class="next-post pull-right"><a href="/Java/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" title="多线程"><img class="cover" src="/img/my/2510c39011c5be704182423e3a695e91.png" onerror="onerror=null;src='/img/404.png'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">多线程</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/Java/Java%E5%9F%BA%E7%A1%80/" title="Java 基础"><img class="cover" src="https://w.wallhaven.cc/full/4o/wallhaven-4oovr5.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-02-27</div><div class="title">Java 基础</div></div></a></div><div><a href="/Java/Java%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F/" title="Java运行时数据区"><img class="cover" src="https://w.wallhaven.cc/full/j8/wallhaven-j8x7ym.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-02-25</div><div class="title">Java运行时数据区</div></div></a></div><div><a href="/Java/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/" title="垃圾回收"><img class="cover" src="https://w.wallhaven.cc/full/n6/wallhaven-n6plgx.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-02-25</div><div class="title">垃圾回收</div></div></a></div><div><a href="/Java/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" title="多线程"><img class="cover" src="/img/my/2510c39011c5be704182423e3a695e91.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-02-28</div><div class="title">多线程</div></div></a></div><div><a href="/Java/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/" title="类加载机制"><img class="cover" src="/img/my/archive.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-03-04</div><div class="title">类加载机制</div></div></a></div></div></div><hr/><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="fas fa-comments fa-fw"></i><span> 评论</span></div></div><div class="comment-wrap"><div><div class="vcomment" id="vcomment"></div></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="/img/avatar.png" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">江一行</div><div class="author-info__description">花有重开日，人无再少年</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">7</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">5</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">2</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/Jjiangyuhang"><i class="fab fa-github"></i><span>关注我</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/Jjiangyuhang" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="/jiang.yu.hang@qq.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a><a class="social-icon" href="https://space.bilibili.com/434232050" target="_blank" title="BiliBili"><i class="fa-brands fa-bilibili"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">一些学习笔记，欢迎交流讨论。</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0"><span class="toc-number">1.</span> <span class="toc-text">概述</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Java-%E5%AE%B9%E5%99%A8%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98"><span class="toc-number">2.</span> <span class="toc-text">Java 容器线程安全问题</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HashMap"><span class="toc-number">3.</span> <span class="toc-text">HashMap</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#JDK7-%E4%B8%8E-JDK8-%E4%B9%8B%E9%97%B4%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-number">3.1.</span> <span class="toc-text">JDK7 与 JDK8 之间的区别</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86"><span class="toc-number">3.2.</span> <span class="toc-text">实现原理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HashMap%E7%9A%84%E6%89%A9%E5%AE%B9%E6%9C%BA%E5%88%B6"><span class="toc-number">3.3.</span> <span class="toc-text">HashMap的扩容机制</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HashMap-%E5%92%8C-HashTable-%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-number">3.4.</span> <span class="toc-text">HashMap 和 HashTable 的区别</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HashMap-%E4%B8%8E-ConcurrentHashMap-%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-number">3.5.</span> <span class="toc-text">HashMap  与 ConcurrentHashMap 的区别</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ConcurrentHashMap"><span class="toc-number">4.</span> <span class="toc-text">ConcurrentHashMap</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#JDK-1-7-%E4%B8%AD%E7%9A%84%E5%AE%9E%E7%8E%B0%EF%BC%9A"><span class="toc-number">4.1.</span> <span class="toc-text">JDK 1.7 中的实现：</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#JDK-1-8%E4%B8%AD%E7%9A%84%E5%AE%9E%E7%8E%B0%EF%BC%9A"><span class="toc-number">4.2.</span> <span class="toc-text">JDK 1.8中的实现：</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%86%E6%AE%B5%E5%88%86%E7%BB%84"><span class="toc-number">4.3.</span> <span class="toc-text">分段分组</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#LinkedHashMap"><span class="toc-number">5.</span> <span class="toc-text">LinkedHashMap</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#TeeMap"><span class="toc-number">6.</span> <span class="toc-text">TeeMap</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ArrayList"><span class="toc-number">7.</span> <span class="toc-text">ArrayList</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%8E-LinkedList-%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-number">7.1.</span> <span class="toc-text">与 LinkedList 的区别</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#CopyOnWriteArrayList"><span class="toc-number">7.2.</span> <span class="toc-text">CopyOnWriteArrayList</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HashSet"><span class="toc-number">8.</span> <span class="toc-text">HashSet</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%8E-TreeSet-%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-number">8.1.</span> <span class="toc-text">与 TreeSet 的区别</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Stream-%E6%B5%81"><span class="toc-number">9.</span> <span class="toc-text">Stream 流</span></a></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/Java/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/" title="类加载机制"><img src="/img/my/archive.jpg" onerror="this.onerror=null;this.src='/img/404.png'" alt="类加载机制"/></a><div class="content"><a class="title" href="/Java/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/" title="类加载机制">类加载机制</a><time datetime="2023-03-04T02:35:24.000Z" title="发表于 2023-03-04 10:35:24">2023-03-04</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/Java/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" title="多线程"><img src="/img/my/2510c39011c5be704182423e3a695e91.png" onerror="this.onerror=null;this.src='/img/404.png'" alt="多线程"/></a><div class="content"><a class="title" href="/Java/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" title="多线程">多线程</a><time datetime="2023-02-28T12:55:06.000Z" title="发表于 2023-02-28 20:55:06">2023-02-28</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/Java/%E9%9B%86%E5%90%88/" title="集合"><img src="https://w.wallhaven.cc/full/lm/wallhaven-lmwg12.jpg" onerror="this.onerror=null;this.src='/img/404.png'" alt="集合"/></a><div class="content"><a class="title" href="/Java/%E9%9B%86%E5%90%88/" title="集合">集合</a><time datetime="2023-02-27T16:22:33.000Z" title="发表于 2023-02-28 00:22:33">2023-02-28</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/Java/Java%E5%9F%BA%E7%A1%80/" title="Java 基础"><img src="https://w.wallhaven.cc/full/4o/wallhaven-4oovr5.png" onerror="this.onerror=null;this.src='/img/404.png'" alt="Java 基础"/></a><div class="content"><a class="title" href="/Java/Java%E5%9F%BA%E7%A1%80/" title="Java 基础">Java 基础</a><time datetime="2023-02-27T13:30:00.000Z" title="发表于 2023-02-27 21:30:00">2023-02-27</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/Java/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/" title="垃圾回收"><img src="https://w.wallhaven.cc/full/n6/wallhaven-n6plgx.jpg" onerror="this.onerror=null;this.src='/img/404.png'" alt="垃圾回收"/></a><div class="content"><a class="title" href="/Java/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/" title="垃圾回收">垃圾回收</a><time datetime="2023-02-25T03:02:39.000Z" title="发表于 2023-02-25 11:02:39">2023-02-25</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2023 By 江一行</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><a id="to_comment" href="#post-comment" title="直达评论"><i class="fas fa-comments"></i></a><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><script>function panguFn () {
  if (typeof pangu === 'object') pangu.autoSpacingPage()
  else {
    getScript('https://cdn.jsdelivr.net/npm/pangu/dist/browser/pangu.min.js')
      .then(() => {
        pangu.autoSpacingPage()
      })
  }
}

function panguInit () {
  if (false){
    GLOBAL_CONFIG_SITE.isPost && panguFn()
  } else {
    panguFn()
  }
}

document.addEventListener('DOMContentLoaded', panguInit)</script><div class="js-pjax"><script>function loadValine () {
  function initValine () {
    const valine = new Valine(Object.assign({
      el: '#vcomment',
      appId: 'vnyeujKjy6AaK5O0RLSaVwC9-gzGzoHsz',
      appKey: 'P0RFxhK58YSXPxBnDRAbZgYd',
      avatar: 'monsterid',
      serverURLs: '',
      emojiMaps: "",
      path: window.location.pathname,
      visitor: false
    }, null))
  }

  if (typeof Valine === 'function') initValine() 
  else getScript('https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js').then(initValine)
}

if ('Valine' === 'Valine' || !false) {
  if (false) btf.loadComment(document.getElementById('vcomment'),loadValine)
  else setTimeout(loadValine, 0)
} else {
  function loadOtherComment () {
    loadValine()
  }
}</script></div><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/activate-power-mode.min.js"></script><script>POWERMODE.colorful = true;
POWERMODE.shake = true;
POWERMODE.mobile = false;
document.body.addEventListener('input', POWERMODE);
</script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>